package com.sharp.oss.service.impl;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.GetObjectRequest;
import com.aliyun.oss.model.OSSObject;
import com.sharp.kernel.exception.ServiceException;
import com.sharp.framework.service.cache.FrameCacheService;
import com.sharp.kernel.utils.DateUtil;
import com.sharp.framework.utils.HttpUtil;
import com.sharp.oss.constant.ObjectStorageConst;
import com.sharp.oss.constant.param.ObjectStorageParamConst;
import com.sharp.oss.dao.entity.SaObjectStorage;
import com.sharp.oss.dao.repository.SaObjectStorageService;
import com.sharp.oss.service.ObjectStorageService;
import com.sharp.oss.vo.UploadVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Calendar;
import java.util.Date;

/**
 * Title: ObjectStorageServiceAliImpl
 * Description: Copyright: Copyright (c) 2019 Company: BHFAE
 *
 * @author Sharp
 * @date 2019/10/16 21:15
 * @description Project Name:
 * @Package: com.srct.service.service.impl
 */
@Slf4j
@Service
public class ObjectStorageServiceAliImpl implements ObjectStorageService {

    private static final String TYPE = "ALI";

    @Resource
    private FrameCacheService frameCacheService;
    @Resource
    private SaObjectStorageService saObjectStorageService;
    @Value("${my.project.name}")
    private String projectName;

    @Value("${ali.oss.endpoint}")
    private String endpoint;

    @Value("${ali.oss.bucket.name}")
    private String bucketName;

    /**
     * 上传文件-全路径(a/b/c/filename)(流的形式-ecs免流量)
     *
     * @param inputStream  文件流
     * @param fileFullPath 文件全路径(保留后缀)
     * @return 保存的文件key
     */
    public String uploadObjectByInputStream(InputStream inputStream, String fileFullPath) throws Exception {
        OSS client = getOss();
        try {
            client.putObject(bucketName, fileFullPath, inputStream);
        } catch (Exception e) {
            throw e;
        } finally {
            client.shutdown();
        }
        return fileFullPath;
    }

    private OSS getOss() {
        String accessKeyId = frameCacheService.getParamValueAvail(ObjectStorageParamConst.ALI_ACCESS_KEY);
        String accessKeySecret = frameCacheService.getParamValueAvail(ObjectStorageParamConst.ALI_ACCESS_SECRET);
        return new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    }

    /**
     * 通过oss->File->key值 获取对应文件的流(字节-网络传输)
     *
     * @param fileKey
     * @return
     */
    public byte[] getFileByteByKey(String fileKey) throws Exception {
        OSS client = getOss();
        InputStream inputStream = null;
        byte[] result = null;
        try {
            OSSObject ossObject = client.getObject(bucketName, fileKey);
            inputStream = ossObject.getObjectContent();
            result = toByteArray(inputStream);
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            client.shutdown();
        }
        return result;
    }

    /**
     * (private)input转字节
     *
     * @param input
     * @return
     * @throws Exception
     */
    private byte[] toByteArray(InputStream input) throws Exception {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[4096];
        int n = 0;
        while (-1 != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
        }
        return output.toByteArray();
    }

    /**
     * 通过key值 获取对应文件的流(InputStream)
     * 实际上这个方法是无效的(client.shutdwon()会将流关闭 )
     * 所以可以这里进行流的操作 (比如可以参见:getFileByteByKey())
     * 把流读取到可以被保存的数据媒介中
     *
     * @param fileKey
     * @return
     */
    @Deprecated
    public InputStream getFileInputStreamByKey(String fileKey) {
        OSS client = getOss();
        InputStream inputStream = null;
        try {
            OSSObject ossObject = client.getObject(bucketName, fileKey);
            inputStream = ossObject.getObjectContent();
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            client.shutdown();
        }
        return inputStream;
    }

    /**
     * 根据文件名获取文件访问URL(外网访问-下行收费)
     *
     * @param ossFileKey 文件名
     * @param expires    URL有效时间（小时）
     * @return
     */
    public String getFileUrl(String ossFileKey, int expires) {
        OSS client = getOss();
        URL url = client.generatePresignedUrl(bucketName, ossFileKey, dateAfter(new Date(), expires, Calendar.HOUR));
        String path = url.toString();
        return path;
    }

    /**
     * (private)获得指定日期之后一段时期的日期。例如某日期之后3天的日期等。
     *
     * @param origDate 基准日期
     * @param amount   基准日期
     * @param timeUnit 时间单位，如年、月、日等。用Calendar中的常量代表
     * @return
     */
    private final Date dateAfter(Date origDate, int amount, int timeUnit) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(origDate);
        calendar.add(timeUnit, amount);
        return calendar.getTime();
    }

    /**
     * 原图生成缩略图url
     *
     * @param ossFileKey
     * @return
     */
    public String getSltFileUrl(String ossFileKey, String centerName) throws IOException {
        OSS client = getOss();
        /**
         * 控制台设置的图片处理的style或者默认style
         */
        String style = "image/auto-orient,1/resize,m_lfit,w_100/quality,q_90";
        GetObjectRequest request = new GetObjectRequest(bucketName, ossFileKey);
        request.setProcess(style);
        OSSObject ossObject = client.getObject(request);
        InputStream inputStream = ossObject.getObjectContent();
        String suffix = ossFileKey.substring(ossFileKey.lastIndexOf("."));
        String name = ossFileKey.substring(0, ossFileKey.lastIndexOf("."));
        String sltName = name + centerName + suffix;
        System.out.println(sltName);
        try {
            client.putObject(bucketName, sltName, inputStream);
        } catch (Exception e) {
            throw e;
        } finally {
            client.shutdown();
        }
        return sltName;
    }

    @Override
    public UploadVO upload(HttpServletRequest request, MultipartFile file, String fileName, String type) {
        String name = file.getOriginalFilename();
        String folderName = frameCacheService.getParamValueAvail(ObjectStorageParamConst.OSS_UPLOAD_PATH) + ObjectStorageConst.SEP + projectName;
        String url;
        if (StringUtils.isNotBlank(type)) {
            folderName = folderName + ObjectStorageConst.SEP + type;
        }
        folderName = folderName + ObjectStorageConst.SEP + DateUtil.formatDate(new Date(), DateUtil.DATE);
        if (StringUtils.isBlank(fileName)) {
            fileName = buildFileName(name, HttpUtil.getRemoteIp(request));
        }
        try {
            url = uploadObjectByInputStream(file.getInputStream(), folderName, fileName);
        } catch (IOException e) {
            throw new ServiceException("上传文件失败", e);
        }
        if (StringUtils.isBlank(type)) {
            type = TYPE;
        }
        record(fileName, type, url, file.getSize());
        return buildUploadVO(fileName, file.getSize(), url);
    }

    /**
     * 上传文件-文件夹拼接(a/b/c/+filename)(流的形式-ecs免流量)
     *
     * @param inputStream 文件流
     * @param folder      文件夹
     * @param fileName    文件名字(保留后缀)
     * @return 保存的文件key
     */
    public String uploadObjectByInputStream(InputStream inputStream, String folder, String fileName) {
        OSS client = getOss();
        //folder+/+fileName
        String key;
        if (StringUtils.isEmpty(folder)) {
            key = fileName;
        } else {
            if (folder.startsWith(ObjectStorageConst.SEP)) {
                key = folder.substring(1) + ObjectStorageConst.SEP + fileName;
            } else {
                key = folder + ObjectStorageConst.SEP + fileName;
            }
        }
        try {
            log.info("添加对象存储: {}", key);
            client.putObject(bucketName, key, inputStream);
        } catch (Exception e) {
            throw e;
        } finally {
            client.shutdown();
        }
        return buildAccessUrl(folder, fileName);
    }

    @Override
    public void delete(Long id) {
        SaObjectStorage os = saObjectStorageService.selectByPrimaryKey(id);
        String domain = frameCacheService.getParamValueAvail(ObjectStorageParamConst.OSS_ACCESS_DOMAIN);
        String url = os.getUri();
        String key = url.substring(domain.length() + 1);
        OSS client = getOss();
        try {
            log.info("删除对象存储: {}", key);
            client.deleteObject(bucketName, key);
        } catch (Exception e) {
            throw e;
        } finally {
            client.shutdown();
        }
        saObjectStorageService.deleteByPrimaryKey(id);
    }
}
